package com.kkkk.rest.common.global;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;

import com.kkkk.rest.common.exception.AppException;
import com.kkkk.rest.common.exception.CommonErrorCodeEnum;
import com.kkkk.rest.common.model.ErrorMessage;
import com.kkkk.rest.common.properties.AppCommonProperties;
import feign.FeignException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.List;


/**
 * GlobalExceptionHandler
 * <p>
 * 微服务全局异常处理器
 *
 * @author szl
 * @date 2018/3/28
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @Autowired
    private AppCommonProperties appCommonProperties;

    @ExceptionHandler(AppException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onAppException(HttpServletRequest request,
                                       HttpServletResponse response,
                                       AppException exception) {
        logger.error("method failed, AppException occur!, code = {}, message = {}", exception.getCode(), exception.getMessage(), exception);

        if (StringUtils.isEmpty(exception.getService())) {
            exception.setService(appCommonProperties.getAppName());
        }
        return new ErrorMessage(exception.getCode(), exception.getMessage(), exception.getFields(), exception.getService());
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onMissingServletRequestParameterException(HttpServletRequest request,
                                                                  HttpServletResponse response,
                                                                  Exception exception) {
        logger.error("method failed. Missing Request Parameter Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.BAD_PARAMETERS.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }

    @ExceptionHandler(MissingPathVariableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onMissingPathVariableException(HttpServletRequest request,
                                                       HttpServletResponse response,
                                                       Exception exception) {
        logger.error("method failed. Missing Path Variable Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.MISSING_PATH_VARIABLE.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onHttpMessageNotReadableException(HttpServletRequest request,
                                                          HttpServletResponse response,
                                                          Exception exception) {
        logger.error("method failed. Missing Required HTTP Body!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.MISSING_REQUIRED_HTTP_BODY.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }


    @ExceptionHandler(DataAccessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onDataAccessException(HttpServletRequest request,
                                              HttpServletResponse response,
                                              Exception exception) {
        logger.error("method failed. Data Access Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.DATA_INTEGRITY_VIOLATION_EXCEPTION.getCode(), "Data Access error", appCommonProperties.getAppName());
    }

    @ExceptionHandler(NumberFormatException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onNumberFormatException(HttpServletRequest request,
                                                HttpServletResponse response,
                                                Exception exception) {
        logger.error("method failed. Number Format Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.NUMBER_FORMAT_EXCEPTION.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage illegalArgumentException(HttpServletRequest request,
                                                 HttpServletResponse response,
                                                 Exception exception) {
        logger.error("method failed. Illegal Argument Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.ILLEGAL_REQUEST.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }

    @ExceptionHandler(FeignException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorMessage onFeignException(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Exception exception) {
        logger.error("method failed. Feign Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.FEIGN_EXCEPTION.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onMethodArgumentTypeMismatchException(HttpServletRequest request,
                                                              HttpServletResponse response,
                                                              Exception exception) {
        logger.error("method failed. Method Argument Type Mismatch Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.ARGUMENT_TYPE_MISMATCH.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onHttpRequestMethodNotSupportedException(HttpServletRequest request,
                                                                 HttpServletResponse response,
                                                                 Exception exception) {
        logger.error("method failed. Http Request Method Not Supported Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.ILLEGAL_REQUEST.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }


    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorMessage onUnknownException(HttpServletRequest request,
                                           HttpServletResponse response,
                                           Exception exception) {
        logger.error("method failed. unknown exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.UNKNOWN_ERROR.getCode(), "internal server error!", appCommonProperties.getAppName());
    }

    /**
     * @Description: 捕获@valid校验异常
     * @Param: [request, response, exception]
     * @Date: 2018/6/19
     */

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage handleValidationException(HttpServletRequest request,
                                                  HttpServletResponse response,
                                                  MethodArgumentNotValidException exception) {
        String message;
        String fieldStr = null;
        FieldError error = exception.getBindingResult().getFieldError();
        if (error != null) {
            JSONObject fields = new JSONObject();
            fields.put("filed", error.getField());
            fields.put("value", error.getRejectedValue());
            fieldStr = JSON.toJSONString(fields);
            message = error.getDefaultMessage();
        } else {
            message = exception.getMessage();
            message = message.substring(message.lastIndexOf("[") + 1, message.lastIndexOf("]") - 1);
        }
        logger.error("Validation Method failed !, message = {}", message, exception);
        return new ErrorMessage(CommonErrorCodeEnum.VALIDATION_ERROR.getCode(), message, fieldStr, appCommonProperties.getAppName());
    }


    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage handleBindException(HttpServletRequest request,
                                            HttpServletResponse response,
                                            BindException exception) {
        String errorMessage;
        String fieldStr = null;
        List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
        if (!CollectionUtils.isEmpty(fieldErrors)) {
            FieldError error = fieldErrors.get(0);
            JSONObject fields = new JSONObject();
            fields.put("filed", error.getField());
            fields.put("value", error.getRejectedValue());
            fieldStr = JSON.toJSONString(fields);
            errorMessage = error.getDefaultMessage();
        } else {
            errorMessage = exception.getMessage();
        }
        return new ErrorMessage(CommonErrorCodeEnum.VALIDATION_ERROR.getCode(), errorMessage, fieldStr, appCommonProperties.getAppName());
    }


    @ExceptionHandler(DuplicateKeyException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onDuplicateKeyException(HttpServletRequest request,
                                                HttpServletResponse response,
                                                DuplicateKeyException exception) {
        logger.error("method failed. DuplicateKeyExceptionException occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.INDEX_KEY_EXIST.getCode(), CommonErrorCodeEnum.INDEX_KEY_EXIST.getMessage(), appCommonProperties.getAppName());
    }

    @ExceptionHandler(DataIntegrityViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorMessage onDataIntegrityViolationException(HttpServletRequest request,
                                                          HttpServletResponse response,
                                                          Exception exception) {
        logger.error("method failed. Data Integrity Violation Exception occur!, message = {}", exception.getMessage(), exception);
        return new ErrorMessage(CommonErrorCodeEnum.DATA_INTEGRITY_VIOLATION_EXCEPTION.getCode(), exception.getMessage(), appCommonProperties.getAppName());
    }


}
